const DIRECT_ATTRIBUTE_MAP = {
  cellpadding: 'cellPadding',
  cellspacing: 'cellSpacing',
  colspan: 'colSpan',
  frameborder: 'frameBorder',
  height: 'height',
  maxlength: 'maxLength',
  nonce: 'nonce',
  role: 'role',
  rowspan: 'rowSpan',
  type: 'type',
  usemap: 'useMap',
  valign: 'vAlign',
  width: 'width',
}
const RGX_NUMERIC_STYLE_UNIT = 'px'
const RGX_NUMERIC_STYLE =
  /^((max|min)?(width|height)|margin|padding|(margin|padding)?(left|top|bottom|right)|fontsize|borderwidth)$/i
const RGX_DEFAULT_VALUE_PROP = /input|textarea|select/i
function localAssertNotFalsy(input, errorMsg = `Input is not of type.`) {
  if (input == null) {
    throw new Error(errorMsg)
  }
  return input
}
const RGX_STRING_VALID = '[a-z0-9_-]'
const RGX_TAG = new RegExp(`^([a-z]${RGX_STRING_VALID}*)(\\.|\\[|\\#|$)`, 'i')
const RGX_ATTR_ID = new RegExp(`#(${RGX_STRING_VALID}+)`, 'gi')
const RGX_ATTR_CLASS = new RegExp(`(^|\\S)\\.([a-z0-9_\\-\\.]+)`, 'gi')
const RGX_STRING_CONTENT_TO_SQUARES = '(.*?)(\\[|\\])'
const RGX_ATTRS_MAYBE_OPEN = new RegExp(`\\[${RGX_STRING_CONTENT_TO_SQUARES}`, 'gi')
const RGX_ATTRS_FOLLOW_OPEN = new RegExp(`^${RGX_STRING_CONTENT_TO_SQUARES}`, 'gi')
export function query(selectors, parent = document) {
  return Array.from(parent.querySelectorAll(selectors)).filter((n) => !!n)
}
export function queryOne(selectors, parent = document) {
  var _a
  return (_a = parent.querySelector(selectors)) !== null && _a !== void 0 ? _a : null
}
export function createText(text) {
  return document.createTextNode(text)
}
export function getClosestOrSelf(element, query) {
  const el = element
  return (
    ((el === null || el === void 0 ? void 0 : el.closest) &&
      ((el.matches(query) && el) || el.closest(query))) ||
    null
  )
}
export function createElement(selectorOrMarkup, attrs) {
  const frag = getHtmlFragment(selectorOrMarkup)
  let element = frag === null || frag === void 0 ? void 0 : frag.firstElementChild
  let selector = ''
  if (!element) {
    selector = selectorOrMarkup.replace(/[\r\n]\s*/g, '')
    const tag = getSelectorTag(selector) || 'div'
    element = document.createElement(tag)
    selector = selector.replace(RGX_TAG, '$2')
    selector = selector.replace(RGX_ATTR_ID, '[id="$1"]')
    selector = selector.replace(
      RGX_ATTR_CLASS,
      (match, p1, p2) => `${p1}[class="${p2.replace(/\./g, ' ')}"]`,
    )
  }
  const selectorAttrs = getSelectorAttributes(selector)
  if (selectorAttrs) {
    for (const attr of selectorAttrs) {
      let matches = attr.substring(1, attr.length - 1).split('=')
      let key = localAssertNotFalsy(matches.shift())
      let value = matches.join('=')
      if (value === undefined) {
        setAttribute(element, key, true)
      } else {
        value = value.replace(/^['"](.*)['"]$/, '$1')
        setAttribute(element, key, value)
      }
    }
  }
  if (attrs) {
    setAttributes(element, attrs)
  }
  return element
}
function getSelectorTag(str) {
  return tryMatch(str, RGX_TAG)
}
function getSelectorAttributes(selector) {
  RGX_ATTRS_MAYBE_OPEN.lastIndex = 0
  let attrs = []
  let result
  while ((result = RGX_ATTRS_MAYBE_OPEN.exec(selector))) {
    let attr = result[0]
    if (attr.endsWith(']')) {
      attrs.push(attr)
    } else {
      attr =
        result[0] + getOpenAttributesRecursive(selector.substr(RGX_ATTRS_MAYBE_OPEN.lastIndex), 2)
      RGX_ATTRS_MAYBE_OPEN.lastIndex += attr.length - result[0].length
      attrs.push(attr)
    }
  }
  return attrs
}
function getOpenAttributesRecursive(selectorSubstring, openCount) {
  let matches = selectorSubstring.match(RGX_ATTRS_FOLLOW_OPEN)
  let result = ''
  if (matches && matches.length) {
    result = matches[0]
    openCount += result.endsWith(']') ? -1 : 1
    if (openCount > 0) {
      result += getOpenAttributesRecursive(selectorSubstring.substr(result.length), openCount)
    }
  }
  return result
}
function tryMatch(str, rgx, index = 1) {
  var _a
  let found = ''
  try {
    found = ((_a = str.match(rgx)) === null || _a === void 0 ? void 0 : _a[index]) || ''
  } catch (e) {
    found = ''
  }
  return found
}
export function setAttributes(element, data) {
  let attr
  for (attr in data) {
    if (data.hasOwnProperty(attr)) {
      setAttribute(element, attr, data[attr])
    }
  }
}
function getHtmlFragment(value) {
  if (value.match(/^\s*<.*?>[\s\S]*<\/[a-z0-9]+>\s*$/)) {
    return document.createRange().createContextualFragment(value.trim())
  }
  return null
}
function getChild(value) {
  if (value instanceof Node) {
    return value
  }
  if (typeof value === 'string') {
    let child = getHtmlFragment(value)
    if (child) {
      return child
    }
    if (getSelectorTag(value)) {
      return createElement(value)
    }
    return createText(value)
  }
  if (value && typeof value.toElement === 'function') {
    return value.toElement()
  }
  return null
}
export function setAttribute(element, attribute, value) {
  let isRemoving = value == null
  if (attribute === 'default') {
    attribute = RGX_DEFAULT_VALUE_PROP.test(element.nodeName) ? 'value' : 'text'
  }
  if (attribute === 'text') {
    empty(element).appendChild(createText(value != null ? String(value) : ''))
  } else if (attribute === 'html') {
    empty(element).innerHTML += value != null ? String(value) : ''
  } else if (attribute == 'style') {
    if (typeof value === 'string') {
      element.style.cssText = isRemoving ? '' : value != null ? String(value) : ''
    } else {
      for (const [styleKey, styleValue] of Object.entries(value)) {
        element.style[styleKey] = styleValue
      }
    }
  } else if (attribute == 'events') {
    for (const [key, fn] of Object.entries(value)) {
      addEvent(element, key, fn)
    }
  } else if (attribute === 'parent') {
    value.appendChild(element)
  } else if (attribute === 'child' || attribute === 'children') {
    if (typeof value === 'string' && /^\[[^\[\]]+\]$/.test(value)) {
      const parseable = value.replace(/^\[([^\[\]]+)\]$/, '["$1"]').replace(/,/g, '","')
      try {
        const parsed = JSON.parse(parseable)
        value = parsed
      } catch (e) {
        console.error(e)
      }
    }
    if (attribute === 'children') {
      empty(element)
    }
    let children = value instanceof Array ? value : [value]
    for (let child of children) {
      child = getChild(child)
      if (child instanceof Node) {
        if (element instanceof HTMLTemplateElement) {
          element.content.appendChild(child)
        } else {
          element.appendChild(child)
        }
      }
    }
  } else if (attribute == 'for') {
    element.htmlFor = value != null ? String(value) : ''
    if (isRemoving) {
      element.removeAttribute('for')
    }
  } else if (attribute === 'class' || attribute === 'className' || attribute === 'classes') {
    element.className = isRemoving ? '' : Array.isArray(value) ? value.join(' ') : String(value)
  } else if (attribute === 'dataset') {
    if (typeof value !== 'object') {
      console.error('Expecting an object for dataset')
      return
    }
    for (const [key, val] of Object.entries(value)) {
      element.dataset[key] = String(val)
    }
  } else if (attribute == 'onclick' && typeof value === 'function') {
    element.addEventListener('click', value)
  } else if (['checked', 'disabled', 'readonly', 'required', 'selected'].includes(attribute)) {
    element[attribute] = !!value
    if (!value) {
      element.removeAttribute(attribute)
    } else {
      element.setAttribute(attribute, attribute)
    }
  } else if (DIRECT_ATTRIBUTE_MAP.hasOwnProperty(attribute)) {
    if (isRemoving) {
      element.removeAttribute(DIRECT_ATTRIBUTE_MAP[attribute])
    } else {
      element.setAttribute(DIRECT_ATTRIBUTE_MAP[attribute], String(value))
    }
  } else if (isRemoving) {
    element.removeAttribute(attribute)
  } else {
    let oldVal = element.getAttribute(attribute)
    if (oldVal !== value) {
      element.setAttribute(attribute, String(value))
    }
  }
}
function addEvent(element, key, fn) {
  element.addEventListener(key, fn)
}
function setStyles(element, styles = null) {
  if (styles) {
    for (let name in styles) {
      setStyle(element, name, styles[name])
    }
  }
  return element
}
function setStyle(element, name, value) {
  name = name.indexOf('float') > -1 ? 'cssFloat' : name
  if (name.indexOf('-') != -1) {
    name = name.replace(/-\D/g, (match) => {
      return match.charAt(1).toUpperCase()
    })
  }
  if (value == String(Number(value)) && RGX_NUMERIC_STYLE.test(name)) {
    value = value + RGX_NUMERIC_STYLE_UNIT
  }
  if (name === 'display' && typeof value !== 'string') {
    value = !!value ? null : 'none'
  }
  element.style[name] = value === null ? null : String(value)
  return element
}
export function empty(element) {
  while (element.firstChild) {
    element.removeChild(element.firstChild)
  }
  return element
}
export function appendChildren(el, children) {
  children = !Array.isArray(children) ? [children] : children
  for (let child of children) {
    child = getChild(child)
    if (child instanceof Node) {
      if (el instanceof HTMLTemplateElement) {
        el.content.appendChild(child)
      } else {
        el.appendChild(child)
      }
    }
  }
}
